﻿const {
    Table,
    Tag,
    Space,
    message,
    Modal,
    Dropdown,
    Button,
    Drawer,
    Form,
    Input,
    Radio,
    DatePicker,
    Checkbox,
    Select,
    Option,
    Menu
} = antd;
const { useEffect, useState, useRef } = React;

function formatTime(time, format = "YYYY-MM-DD HH:mm:ss") {
    if (time == null || time == undefined) {
        return '--';
    }
    return dayjs(time).format(format)
}

var ee = new EventEmitter();
var lisenterKey = "refreshData";
var editKey = "editJob";

var pauseJobKey = "pauseJobKey";
var startJobKey = "startJobKey";
var stopJobKey = "stopJobKey";
var rescheduleJobKey = "rescheduleJobKey";
var deleteJobKey = "deleteJobKey";

var optKey = {
    second: "second",
    minute: "minute",
    hour: "hour",
    day: "day"
}

function operateJob(name, groupName, operation, key) {
    message.loading({ content: `${operation} job, please waiting....`, key });
    return axios.get(`/job/${operation}Job`, { params: { name, groupName } })
        .then(() => {
            message.success({ content: `${operation} job success`, key })
        })
        .catch(() => {
            message.error({ content: `${operation} job failed`, key })
        })
        .finally(() => ee.emit(lisenterKey));
}

function startJob(name, groupName) {
    operateJob(name, groupName, 'start', startJobKey);
}

function rescheduleJob(name, groupName) {
    Modal.confirm({
        content: "job will be re start!!!",
        onOk() {
            operateJob(name, groupName, 'reschedule', rescheduleJobKey);
        },
    })
}

function pauseJob(name, groupName) {
    operateJob(name, groupName, 'pause', stopJobKey);
}

function deleteJob(name, groupName) {
    Modal.confirm({
        content: "job will be deleted and will never invocation!!!",
        onOk() {
            operateJob(name, groupName, 'delete', deleteJobKey);
        },
    })
}

function renderJobState(state) {
    switch (state) {
        case 0: return <Tag color="#87d068">running</Tag>;
        case 1: return <Tag color="#cd201f">paused</Tag>;
        case 2: return <Tag color="success">completed</Tag>;
        case 3: return <Tag color="error">error</Tag>;
        case 4: return <Tag color="warning">blocked</Tag>;
        case 5: return <Tag color="default">none</Tag>;
        default: return <Tag>no tag support</Tag>;
    }
}

function viewJobDetail(jobDetail) {
    if (!jobDetail.config) {
        jobDetail.config = {
            repeatForever: false,
            intervalType: 0,
            interval: 0,
            executeCount: 1,
            start: null,
            end: null
        }
    }
    ee.emit(editKey, jobDetail)
}

function renderAction({ state, name, groupName, jobDetail, type }) {
    var style = { width: 20, cursor: 'pointer' };
    jobDetail.type = type;
    var images = []
    let pauseDom = <img src="/images/pause.svg" style={style} onClick={() => pauseJob(name, groupName)}></img>;
    let startDom = <img src="/images/start.svg" style={style} onClick={() => startJob(name, groupName)}></img>;
    let refreshDom = <img src="/images/refresh.svg" style={style} onClick={() => rescheduleJob(name, groupName)}></img>;
    let deleteDom = <img src="/images/delete.svg" style={style} onClick={() => deleteJob(name, groupName)}></img>;
    let editDom = <img src="/images/edit.svg" style={style} onClick={() => viewJobDetail(jobDetail)}></img>;
    images.push(editDom);
    switch (state) {
        case 0:
            images.push(pauseDom);
            images.push(refreshDom);
            images.push(deleteDom);
            break;
        case 1:
            images.push(startDom);
            images.push(refreshDom);
            images.push(deleteDom);
            break;
        case 2:
            images.push(refreshDom);
            images.push(deleteDom);
            break;
        case 3: break;
        case 4: break;
        case 5: break;
        default: break;
    }
    return images;
}

const columns = [
    {
        title: '任务名',
        dataIndex: 'name',
        key: 'name',
    },
    {
        title: '任务分组',
        dataIndex: 'groupName',
        key: 'groupName',
    },
    {
        title: '执行状态',
        dataIndex: 'groupName',
        key: 'groupName',
        render: (text, record, index) => {
            var state = record.state;
            return (
                <div>{renderJobState(state)}</div>
            )
        }
    },
    {
        title: '请求网址',
        dataIndex: 'url',
        key: 'url',
        render: text => <Button type="link" href={text}>{text}</Button>
    },
    {
        title: '任务描述',
        dataIndex: 'description',
        key: 'description',
    },
    {
        title: '开始时间',
        dataIndex: 'startTime',
        key: 'startTime',
        render: txt => formatTime(txt)
    },
    {
        title: '结束时间',
        dataIndex: 'endTime',
        key: 'endTime',
        render: txt => formatTime(txt)
    },
    {
        title: '上次执行时间',
        dataIndex: 'previousFireTime',
        key: 'previousFireTime',
        render: txt => formatTime(txt)
    },
    {
        title: '下次执行时间',
        dataIndex: 'nextFireTime',
        key: 'nextFireTime',
        render: txt => formatTime(txt)
    },
    {
        title: '操作',
        key: 'action',
        render: (text, record, index) => {
            return (
                <div style={{ display: 'flex', justifyContent: 'space-around' }}>
                    {renderAction(record)}
                </div>
            )
        },
    },
];

var taskType = {
    cron: 'cron',
    simple: 'simple'
}

var defaultTime = '1900-01-01'
var defaultFormat = 'YYYY-MM-DD HH:mm:ss';

function CronConfig({ jobDetail }) {
    var [validateStatus, setvalidateStatus] = useState('');
    return (
        <div>
            <Form.Item name="cron"
                label="cron表达式"
                validateTrigger={'onBlur'}
                hasFeedback
                validateStatus={validateStatus}
                rules={[
                    { required: true, message: "cron expression is required" },
                    ({ getFieldValue }) => ({
                        validator(_, value) {
                            setvalidateStatus('validating')
                            return axios
                                .get("/job/validate", {
                                    params: {
                                        cronExpression: value
                                    }
                                })
                                .then(result => {
                                    if (result.data != null & result.data.validStatus) {
                                        setvalidateStatus('success')
                                        return Promise.resolve();
                                    }
                                    else {
                                        setvalidateStatus('error')
                                        return Promise.reject(new Error("cron expression is not valid"));
                                    }
                                })
                        },
                    }),
                ]}>
                <Input defaultValue={jobDetail.cron} value={jobDetail.cron} />
            </Form.Item>
        </div>
    )
}


var opts = [
    { label: "每一秒", value: 0 },
    { label: "每一分", value: 1 },
    { label: "每一小时", value: 2 },
    { label: "每一天", value: 3 }]

function range(start, end) {
    const result = [];
    for (let i = start; i < end; i++) {
        result.push(i);
    }
    return result;
}

function disabledDate(current) {
    return current && current < moment().endOf('day');
}

function disabledDateTime() {
    return {
        disabledHours: () => range(0, 24).splice(4, 20),
        disabledMinutes: () => range(30, 60),
        disabledSeconds: () => [55, 56],
    };
}


var repeatForeverOpts = [
    { label: "永久执行", value: true },
    { label: "非永久执行", value: false }];
function SimpleConfig({ jobDetail }) {
    if (jobDetail == null || jobDetail == undefined || Object.getOwnPropertyNames(jobDetail).length == 0 || jobDetail.type == taskType.cron) {
        jobDetail = {
            config: {}
        }
    }

    const repeatForever = jobDetail.config.repeatForever || false;
    const [_repeatForever, set_repeatForever] = useState(repeatForever);
    var defaultIntervalTypeValue = jobDetail.config.intervalType;

    return (
        <div>
            <Form.Item name="repeatForever" label="永久执行">
                <Select allowClear={false} defaultValue={repeatForever} value={repeatForever} options={repeatForeverOpts} onSelect={ value => set_repeatForever(value) }>
                </Select>
            </Form.Item>
            <Form.Item name="intervalType" label="执行周期">
                <Select allowClear={false} defaultValue={defaultIntervalTypeValue} value={defaultIntervalTypeValue} options={opts}>
                </Select>
            </Form.Item>
            <Form.Item name="interval" label="执行频次">
                <Input defaultValue={jobDetail.config.interval} value={jobDetail.config.interval} />
            </Form.Item>
            <Form.Item name="executeCount" label="执行次数">
                <Input disabled={_repeatForever} defaultValue={jobDetail.config.executeCount} value={jobDetail.config.executeCount} />
            </Form.Item>
        </div>
    )
}

const TaskConfigForm = ({ onTaskTypeChange, onFinish, _taskType, createingJob, jobDetail = {}, submitBtnText = '创建新任务' }) => {
    if (!jobDetail.config || isPlainObject(jobDetail.config)) {
        jobDetail.config = {}
    }
    var _jobDetail = {
        type: jobDetail.type || '',
        url: jobDetail.url || '',
        groupName: jobDetail.groupName || '',
        description: jobDetail.description || '',
        name: jobDetail.name || '',
        repeatForever: jobDetail.config.repeatForever || false,
        intervalType: 0,
        interval: jobDetail.config.interval || 1,
        executeCount: jobDetail.config.executeCount || 1,
        cron: jobDetail.cron || '',
    }
    return (
        <Form initialValues={_jobDetail} wrapperCol={{ span: 19 }} labelCol={{ span: 5 }} onFinish={onFinish}>
            <Form.Item name="name" label="任务名" rules={[{ required: true, message: 'task name is required' }]}>
                <Input disabled={jobDetail.name == ''} defaultValue={jobDetail.name} value={jobDetail.name} />
            </Form.Item>
            <Form.Item name="url" label="任务内容" rules={[
                { required: true, message: 'url is required' },
                { pattern: /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/, message: 'url is not valid' }
            ]}>
                <Input placeholder="请输入要请求的url，仅限get" defaultValue={jobDetail.url} value={jobDetail.url} />
            </Form.Item>
            <Form.Item name="groupName" label="分组名">
                <Input disabled={jobDetail.groupName == ''} defaultValue={jobDetail.groupName} value={jobDetail.groupName} />
            </Form.Item>
            <Form.Item name="description" label="任务描述">
                <Input defaultValue={jobDetail.description} value={jobDetail.description} />
            </Form.Item>
            <Form.Item name="type" label="任务类型" rules={[{ required: true, message: 'task type is required' }]}>
                <Radio.Group onChange={onTaskTypeChange} defaultValue={jobDetail.type} value={jobDetail.type}>
                    <Radio value={taskType.cron}>cron</Radio>
                    <Radio value={taskType.simple}>simple</Radio>
                </Radio.Group>
            </Form.Item>
            {
                _taskType == taskType.simple && <SimpleConfig jobDetail={jobDetail} />
            }
            {
                _taskType == taskType.cron && <CronConfig jobDetail={jobDetail} />

            }
            <Form.Item wrapperCol={{ offset: 8 }}>
                <Button loading={createingJob} type="primary" htmlType="submit">
                    {submitBtnText}
                </Button>
                <Button type="default" htmlType="submit" style={{ margin: '0 16px' }}>
                    取消
                </Button>
            </Form.Item>
        </Form>
    )
}

const TaskList = () => {
    const [dataSource, setDataSource] = useState([]);
    const [loading, setLoading] = useState(true);
    const [refresh, setRefresh] = useState(true);
    const [visible, setVisible] = useState(false);
    const [editFormVisible, setEditFormVisible] = useState(false);
    const [createingJob, setCreatingJob] = useState(false);
    const [_taskType, setTaskType] = useState('');
    const [jobDetail, setJobDetail] = useState({});

    const getData = () => {
        axios.get("/job/jobs")
            .then(result => {
                if (result.data != null && result.data.length >= 0) {
                    setDataSource(result.data);
                }
            })
            .catch(e => {
                message.info('something has wrong with infomation: ' + e.message);
            })
            .finally(() => setLoading(false));
    }

    const refreshFn = () => setRefresh(!refresh);

    const editFn = (job) => {
        setEditFormVisible(true);
        console.log(job);
        setTaskType(job.type);
        setJobDetail(job);
    }

    useEffect(() => {
        ee.addListener(editKey, editFn);
        return () => ee.removeListener(editKey, editFn);
    }, [jobDetail]);
    useEffect(() => {
        getData();
        ee.addListener(lisenterKey, refreshFn);
        return () => ee.removeListener(lisenterKey, refreshFn);
    }, [refresh]);

    const showDrawer = () => {
        setVisible(true);
        setJobDetail({ config: {} });
        setEditFormVisible(false);
        setTaskType('');
    };

    const onClose = () => {
        setVisible(false);
        setTaskType('');
        setEditFormVisible(false);
        setJobDetail({ config: {} });
    }

    const onTaskTypeChange = (e) => {
        setTaskType(e.target.value);
    }

    const handleFormData = (value) => {
        const data = {
            type: value.type,
            description: value.description,
            url: value.url,
            groupName: value.groupName || 'DEFAULT',
            name: value.name,
            priority: 0,
            cron: value.cron,
            interval: value.interval || 1,
            intervalType: value.intervalType || 0,
            executeCount: value.executeCount || 1,
            repeatForever: value.repeatForever,
        };

        return data;
    }

    const createJob = (value) => {
        var data = handleFormData(value);
        setCreatingJob(true);
        axios
            .post('/job/new', data)
            .then(result => {
                result.status == 200 ? message.success("创建任务成功") : message.error("创建任务失败");
                onClose();
                ee.emitEvent(lisenterKey);
            })
            .catch(e => {
                console.log(e);
                message.error("somthing has wrong");
            })
            .finally(() => {
                setCreatingJob(false);
            })
    }

    const updateJob = (value) => {
        var data = handleFormData(value);
        setCreatingJob(true);
        axios
            .post('/job/edit', data)
            .then(result => {
                result.status == 200 ? message.success("编辑任务成功") : message.error("编辑任务失败");
                onClose();
                ee.emitEvent(lisenterKey);
            })
            .catch(e => {
                message.error("somthing has wrong");
            })
            .finally(() => {
                setCreatingJob(false);
            })
    }

    return (
        <div>
            <Button onClick={showDrawer} type={'primary'}>添加新任务</Button>
            <Table
                columns={columns}
                dataSource={dataSource}
                loading={loading}
                scroll={{ x: 1500, y: 600 }}
                onRow={record => {
                    return {
                        onContextMenu: e => handleContextMenu(e, record),
                    };
                }}
            />
            <Drawer
                title="添加新任务"
                placement="right"
                closable={true}
                onClose={onClose}
                visible={visible}
                destroyOnClose={true}
                width={480}
            >
                <TaskConfigForm
                    onTaskTypeChange={onTaskTypeChange}
                    onFinish={createJob}
                    _taskType={_taskType}
                    createingJob={createingJob} />
            </Drawer>
            <Drawer
                title="编辑任务"
                placement="right"
                closable={true}
                onClose={onClose}
                visible={editFormVisible}
                destroyOnClose={true}
                width={480}
            >
                <TaskConfigForm
                    onTaskTypeChange={onTaskTypeChange}
                    onFinish={updateJob}
                    _taskType={_taskType}
                    jobDetail={jobDetail}
                    submitBtnText={'编辑任务'}
                    createingJob={createingJob} />
            </Drawer>
        </div>
    )
}
render(<TaskList />, "all-tasks");